﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net;
using System.Runtime.Serialization.Json;
using System.IO;

namespace MyNet.WebUI {

    using DotNetOpenAuth.AspNet.Clients;
    using DotNetOpenAuth.Messaging;
    using Microsoft.AspNet.Membership.OpenAuth;

    public class FacebookOAuthClient : DotNetOpenAuth.AspNet.Clients.OAuth2Client {

        private const String AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth";
        private const String TokenEndpoint = "https://graph.facebook.com/oauth/access_token";

        private readonly string appID;
        private readonly string appSecret;
        private readonly string authScope;

        public FacebookOAuthClient( String applicationID, String applicationSecret, String authScope )
            : base( "Facebook" ) {
            this.appID = applicationID;
            this.appSecret = applicationSecret;
            this.authScope = authScope;
        } // Constructor

        protected override Uri GetServiceLoginUrl( Uri returnUrl ) {

            var builder = new UriBuilder( AuthorizationEndpoint );

            MessagingUtilities.AppendQueryArgument( builder, "client_id", this.appID );
            MessagingUtilities.AppendQueryArgument( builder, "redirect_uri", returnUrl.AbsoluteUri );
            MessagingUtilities.AppendQueryArgument( builder, "scope", this.authScope );

            return builder.Uri;

        } // Method

        protected override IDictionary<string, string> GetUserData( string accessToken ) {

            FacebookGraphData graphData;

            var request =
                WebRequest.Create(
                    "https://graph.facebook.com/me?access_token=" + accessToken );

            using ( var response = request.GetResponse() ) {
                using ( var responseStream = response.GetResponseStream() ) {

                    var serializer = new DataContractJsonSerializer( typeof( FacebookGraphData ) );
                    graphData = (FacebookGraphData) serializer.ReadObject( responseStream );
                }
            }

            // Dictionary containing the public profil of the Facebook User
            var userData = new Dictionary<String, String>();
            userData.Add( "id", graphData.Id );
            userData.Add( "username", graphData.Email );
            userData.Add( "name", graphData.Name );
            userData.Add( "link", graphData.Link == null ? null : graphData.Link.AbsoluteUri );
            userData.Add( "gender", graphData.Gender );
            userData.Add( "birthday", graphData.Birthday );
            userData.Add( "accessToken", accessToken );

            return userData;

        } // Method

        protected override string QueryAccessToken( Uri returnUrl, string authorizationCode ) {

            var builder = new UriBuilder( TokenEndpoint );

            MessagingUtilities.AppendQueryArgument( builder, "client_id", this.appID );
            MessagingUtilities.AppendQueryArgument( builder, "redirect_uri", NormalizeHexEncoding( returnUrl.AbsoluteUri ) );
            MessagingUtilities.AppendQueryArgument( builder, "client_secret", this.appSecret );
            MessagingUtilities.AppendQueryArgument( builder, "code", authorizationCode );
            MessagingUtilities.AppendQueryArgument( builder, "scope", this.authScope );

            using ( WebClient client = new WebClient() ) {
                string data = client.DownloadString( builder.Uri );
                if ( string.IsNullOrEmpty( data ) ) {
                    return null;
                }

                var parsedQueryString = HttpUtility.ParseQueryString( data );
                return parsedQueryString["access_token"];
            }

        } // Method

        private static String NormalizeHexEncoding( string url ) {

            var chars = url.ToCharArray();

            for ( int i = 0; i < chars.Length - 2; i++ ) {
                if ( chars[i] == '%' ) {
                    chars[i + 1] = char.ToUpperInvariant( chars[i + 1] );
                    chars[i + 2] = char.ToUpperInvariant( chars[i + 2] );
                    i += 2;
                }
            }

            return new String( chars );
        } // Method


    } // Class

}